package cn.maihe.elg.operation.service.order;

import cn.maihe.elg.operation.model.enums.NoticeTypeEnum;
import cn.maihe.elg.operation.repository.entity.NoticeRetryInfo;
import cn.maihe.elg.operation.repository.entity.NoticeRetryInfoCondition;
import cn.maihe.elg.operation.service.BaseDaoService;
import cn.maihe.elg.operation.utils.IdGenerate;
import cn.maihe.framework.starter.mybatis.mapper.PlusEntityWrapper;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.Date;

/**
 * @Description
 * @Author WJH
 * @Date 2021/10/27
 */
@Service
@Slf4j
public class NoticeRetryInfoService extends BaseDaoService<NoticeRetryInfo> {

    /**
     * @param type          通知类型
     * @param centerId      中心id 获取中心及通知类型的实现类 发起重试通知
     * @param orgId         机构id暂未使用
     * @param acceptOrderNo 受理订单号
     * @param reqBody       通知业务报文json字符串
     * @param nextTime      下次通知时间 轮询当前时间>nextTime && retryTimes < maxRetryTimes
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public <REQ, RESP> void saveOrUpdateNoticeRetryInfo(NoticeTypeEnum type, Long centerId, Long orgId, String acceptOrderNo, REQ reqBody, Instant nextTime, RESP respBody) {
        // noticeType+acceptOrderNo 唯一
        NoticeRetryInfo info = NoticeRetryInfo.builder()
                .noticeType(type.getType()).centerId(centerId).orgId(orgId)
                .acceptOrderNo(acceptOrderNo)
                .respBodyJson(JSON.toJSONString(respBody))
                .build();
        //仅有reqBody不为空才设值添加|更新
        if (reqBody != null) {
            info.setReqBodyJson(JSON.toJSONString(reqBody));
            info.setReqBodyClass(reqBody.getClass().getName());
        }

        NoticeRetryInfo dbInfo = this.getNoticeRetryInfo(type, acceptOrderNo);
        if (dbInfo == null) {
            info.setId(IdGenerate.getId());
            //定时轮询有个最大通知次数限制 默认5次
            info.setRetryTimes(0);
            //下次间隔周期秒
            long periodSeconds = buildPeriodSeconds(info.getRetryTimes());
            info.setNextTime(Date.from(Instant.now().plusSeconds(periodSeconds)));
            this.insert(info);
        } else {
            info.setId(dbInfo.getId());
            info.setRetryTimes(dbInfo.getRetryTimes() + 1);
            //下次间隔周期秒
            long periodSeconds = buildPeriodSeconds(info.getRetryTimes());
            info.setNextTime(Date.from(Instant.now().plusSeconds(periodSeconds)));
            this.updateById(info);
        }
        log.info("重试通知type:{}-acceptOrderNo:{} >> 当前通知表记录JSON={}",info.getNoticeType(),info.getAcceptOrderNo(),JSON.toJSONString(info));
    }

    private long buildPeriodSeconds(Integer retryTimes) {
        long periodSeconds = 60L;
        if (retryTimes == null || retryTimes.intValue() == 0) {
            return periodSeconds;
        }
        if (retryTimes < 5) {
            // 1min 3min 7min 9min
            periodSeconds = (retryTimes * 2 + 1) * periodSeconds;
        } else {
            //30min
            periodSeconds = 30 * periodSeconds;
        }

        return periodSeconds;
    }

    public NoticeRetryInfo getNoticeRetryInfo(NoticeTypeEnum typeEnum, String acceptOrderNo) {
        PlusEntityWrapper<NoticeRetryInfo> condition = NoticeRetryInfoCondition.builder().andNoticeTypeEq(typeEnum.getType())
                .andAcceptOrderNoEq(acceptOrderNo).build();
        return this.selectOne(condition);
    }


    public void deleteByTypeAndOrderNo(NoticeTypeEnum typeEnum, String acceptOrderNo) {
        PlusEntityWrapper<NoticeRetryInfo> condition = NoticeRetryInfoCondition.builder().andNoticeTypeEq(typeEnum.getType())
                .andAcceptOrderNoEq(acceptOrderNo).build();
        this.delete(condition);
    }
}
